﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Threading;
using Userslib;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace IRCTest
{
    /* IRCCommands
     * Handle method invokes the passed command (as a string) by using reflection.
     * 
     * In the class there are several pre-defined commands implemented, new command can be created and invoked without any further difficulties
     */
    public class IRCCommands : IRCActions
    {
        
        
        private UserList Users = new UserList();
        
        String PLink = "http://ts1.travian.nl/spieler.php?uid=";
        String VLink = "http://ts1.travian.nl/karte.php?d=";
        public IRCCommands(ref StreamReader Reader, ref StreamWriter Writer, String Channel,String Nick,String User,String ChanWatch)
            : base(ref Reader, ref Writer, Capitalize(Channel), Capitalize(ChanWatch), Nick)
        {
            set_Ident(User);
            set_Nick(Nick);
        }

        public void Handle(String Command,User User,String Receipient,String[] Parameters)
        {

            try
            {
                Command = Command[0].ToString().ToUpper() + Command.ToLower().Substring(1);
                Type thisType = this.GetType();
                Object[] Params = CombineParameters(Receipient, User, Parameters) ;
                MethodInfo theMethod = thisType.GetMethod(Command);
                if (Params[2].Equals(String.Empty))
                    Params = new Object[] { Params[0],Params[1]};
                theMethod.Invoke(this, (object[])(Params));
            }
            catch (TargetParameterCountException Exc)
            {
                Send_Message2("Invalid use of the command " + Command + " invalid parameters specified.", Receipient);
            }

            catch (ArgumentException Member)
            {
                Send_Message2(Member.Message, Channel);
            }

            catch (MemberAccessException Exc)
            {
                Send_Message2(Exc.Message + " Please contact an OP to do so.", Receipient);
            }
            catch (Exception Exceptions)
            {
                if (Exceptions.Message.ToLower().Contains("mysql"))
                    Notice("Please report the following error to Daneo : " + Exceptions.Message, User.Username);
                
                Notice(Exceptions.Message + " occured", Receipient);
                if (Command.Length < 15)
                    Notice(String.Concat(Command, " was an invalid command."), Receipient);
                else
                    Notice(".. tldr ..", Receipient);
            }
            
            //switch(Command){
            //    case 
        }

        public void Getmember(String Channel, User Member,String Name)
        {
            if (Name.Length < 20)
                try
                {
                    Name = Capitalize(Name);
                    Name = Replace(Name, ' ');
                    if (!Users.Contains(Name))
                        if (User.isUser(Name))
                        {
                            Player MemberData = new Player(Name);
                            //Users.Add(MemberData);
                            Users.Add(MemberData);
                            Send_Message2(Name + " : " + PLink + MemberData.PID, Channel);
                            Send_Message2("Race : "+ MemberData.Tribe, Channel);
                            Send_Message2("Population : " + MemberData.Population, Channel);
                            
                        }
                        else
                            Send_Message2(Name + " is not a member according to my database. ", Channel);
                    else
                    {
                        var user = Users.Where(x => x.Equals(Name)).FirstOrDefault();
                        Send_Message2(Name + " : " + PLink + user.PID, Channel);
                        Send_Message2("Population : " + user.Population, Channel);
                    }
                   
                }
                catch (IndexOutOfRangeException Exc)
                {
                    Send_Message2("Unable to find the user '" + Name + "'. Please check for spelling mistakes if you're certain the member exists!",Channel);
                }
            else
                Notice("Not even trying .. I wouldn't find it here anyway ..", Channel);
        }

        public void Refresh(String Channel,User Member)
        {
            Send_Message2("Commencing update, please bear with me ..",Channel);
            Task Alliance = Task.Factory.StartNew(new Action(() =>
            {
                Alliances.Update();
                Send_Message2("Alliances done ..", Channel);
            }));
            Task Users = Task.Factory.StartNew(new Action(() =>
            {
                User.UpdateUsers();
                Send_Message2("Users done ..", Channel);
            }));
            Task.WaitAll(new Task[] {Alliance,Users});
            Member.Update_Commands();
            Send_Message2("Updated " + Member.Username + "'s commands.", Channel);    
            
        }

    
        public void Addmember(String Channel , User Lid, String Member)
        {
            Member = Replace(Member,' ');
            String[] Details = Member.Split(' ');
            if (!User.isUser(Capitalize(Details[0])))
                try
                {
                    if (int.Parse(Details[1].ToString()) > Lid.Accesslvl)
                        throw new Exception("Access level cannot be above your own.");
                    Details[0] = Capitalize(Details[0]);
                    UserReg NewUser = new UserReg(Details[0], int.Parse(Details[1].ToString()),Details[2].ToString());
                    if (NewUser.Register())
                    {
                        Send_Message2(Details[0] + " was succesfully added!", Channel);
                        //User.GetUsers();
                    }
                    else
                        Send_Message2("Something has gone wrong ..", Channel);
                }
                catch (IndexOutOfRangeException)
                {
                    Send_Message2("Invalid Accesslvl specified.", Channel);
                }
                catch (ArgumentException Exc)
                {
                    Send_Message2(Exc.Message, Channel);
                }
                catch (InvalidCastException Exc)
                {
                    Send_Message2("Invalid Accesslvl specified.", Channel);
                }
                catch (Exception Exc)
                {
                    Send_Message2(Exc.Message, Channel);
                }

            else
                Send_Message2(Details[0] + " is already a member according to my filed memory .. ",Channel);
        }

        public void Farm(String Channel, User Member, String Target)
        {
            
            foreach (KeyValuePair<String,int> Unit in Member.getAttackingTroops())
            {
                String Link = "http://ts1.travian.nl/a2b.php?";
                int ID = Unit.Value;
                while (ID > 9)
                    ID = ID - 10;
                Link += "dname=" + Target + "&t" + ID + "=10";
                Send_Message2("If you wish to farm with " + Unit.Key + " use the following link : " + Link, Member.Username);
               
               // Send_Message2(
            }

        }
        public void Travian(String Channel, User Member )
        {
            Send_Message2("Travian village : http://ts1.travian.nl/dorf2.php",Channel);
            Send_Message2("Travian resources : http://ts1.travian.nl/dorf1.php",Channel);
        }
        public void Troepen(String Channel, User Member)
        {
            Send_Message2("Troepentool : http://www.gettertools.com/ts1.travian.nl/FF,groups88", Channel);
        }
        public void Forum(String Channel, User Member)
        {
            Send_Message2("Forum : http://www.maniacnetworx.nl/ff",Channel);
        }
        public void Chat(String Channel, User Member)
        {
            Send_Message2("Chat : http://www.maniacnetworx.nl/ff/chat",Channel);
        }

        public void Invite(String Channel, User Member,String Person)
        {
            
            Inviter(Channel, Person);
           // Mode(Line,'-','v');
        }

        public void Findally(String Channel, User Member,String Ally )
        {
            Replace(Ally, ' ');
            Ally = Capitalize(Ally);
            String ALink = "http://ts1.travian.nl/allianz.php?aid=";
            if (Alliances.Alliance_Stats.Count == 0)
            Alliances.Update();
            if (Ally.Length < 15 && Userslib.Alliances.AllianceDict.ContainsValue(Ally))
            {
                var key = from AID in Alliances.AllianceDict where(AID.Value.Equals(Ally)) select AID.Key;
                try
                {
                    var Alliance = Alliances.Alliance_Stats[key.FirstOrDefault()];
                    Send_Message2(Ally + " : " + ALink + key.FirstOrDefault(), Channel);
                    Send_Message2(Ally + " has " + Alliance.Members + " members, an average population of" + Alliance.Top + ", having a top player with " + Alliance.Pop + " population.", Member.Username);
                }
                catch
                {
                    Send_Message2(Ally + " threw up on me..", Channel);
                }
            }
            else
                Notice("I refuse to work at that ..", Channel);
        }

        public void Findplayer(String Channel, User Member, String Player)
        {
            if (Player.Length < 20)
            {

                Player P;
                if (!Users.Contains(Capitalize(Player)))
                    P = new Userslib.Player(Player);
                else
                    P = Users.Where(x => ((x as Userslib.Player) != null) && (x as Userslib.Player).Name == Capitalize(Player)).FirstOrDefault();
                if (P != null)
                    if (P.isValid){
                        Users.Add(P);
                        Send_Message2("Player " +P.Name + ": " + PLink + P.PID, Channel);
                        Send_Message2(P.Name + " has " + P.VillageList.Count + " villages.", Channel);
                        foreach (Userslib.localhost.Village Village in P.VillageList)
                        {
                            Send_Message2(Village.Name + " : " + VLink + Village.VID, Channel);
                        }
                    }
                else
                    Send_Message2("Sorry, " + P.Name + " not found.",Channel);
            }
            else
                Notice("Go bug someone else ..", Channel);
           
        }


        public void Getdistance(String Channel, User Member, String Coords)
        {            
            Coords = Replace(Coords, ' ');
            String[] Parameters = Coords.Split(' ');
            
            String Dorp1 = Parameters[0];
            String Dorp2 = Parameters[1];
            try
            {
                double x1 = double.Parse(Dorp1.Split('|')[0]);
                double y1 = double.Parse(Dorp1.Split('|')[1]);
                double x2 = double.Parse(Dorp2.Split('|')[0]);
                double y2 = double.Parse(Dorp2.Split('|')[1]);
                //d = (((x2-x1)^2)+((y2-y1)^2))^0.5
                double Res = getDistance(x1, y1, x2, y2);
                bool BeginSet = false;
                bool EndSet = false;
                if (Parameters.Length == 4)
                {
                    if (Parameters[2].ToLower().Equals("b"))
                        BeginSet = true;
                
                    if (Parameters[2].ToLower().Equals("e"))
                        EndSet = true;
                }
                else if (Parameters.Length < 2 || Parameters.Length > 4)
                    throw new Exception("Invalid options specified.");


                Send_Message2("The distance between " + Dorp1 + " and " + Dorp2 + " is " + (int)Res + " tile(s)", Channel);
                
                Dictionary<String, int> Troops = Member.GetTroops();
                Dictionary<String, int> To_Late = new Dictionary<string, int>();
                foreach (KeyValuePair<String, int> Unit in Troops)
                {
                    double Travelling = Res / Unit.Value;


                    TimeSpan Trav_Time = getTime(Travelling);
                   if (!BeginSet && Parameters.Length == 2)
                        Send_Message2("\u000303" + Unit.Key + "\u000304 would travel \u000303" + FormatDate(Trav_Time), Member.Username);
                   else if (BeginSet){
                       TimeSpan Leaving = DateTime.Parse(Parameters[3].ToString()).TimeOfDay;
                       TimeSpan Arrival = Trav_Time + Leaving;
                       Send_Message2("\u000303" + Unit.Key + "\u000304 would arrive on \u000303 " +DateTime.Now.AddDays(Arrival.Days).ToShortDateString() + " at " + FormatDate(Arrival), Member.Username);
                   }
                   else if (EndSet){
                       if (Parameters.Length == 4)
                       {
                           TimeSpan Arrival = DateTime.Parse(Parameters[3].ToString()).TimeOfDay;
                           DateTime Date_Arriv ;
                           if (Arrival < DateTime.Now.TimeOfDay)
                           {
                               //maken dat tijd kleiner dan vandaag als morgen wordt bezien
                               Arrival = new TimeSpan(Arrival.Days,Arrival.Hours,Arrival.Minutes,Arrival.Seconds);
                               //Arrival.Add = new TimeSpan(DateTime.Now.AddDays(1).Subtract(DateTime.Now.TimeOfDay).ToLongTimeString());
                               break;

                           }
                           TimeSpan Start = Arrival - Trav_Time;
                           TimeSpan Reached = DateTime.Now.TimeOfDay + Trav_Time;
                           
                           if (Start < DateTime.Now.TimeOfDay)
                               To_Late.Add(Unit.Key, Unit.Value);
                           else
                           {
                               String Part_Time = FormatDate(Start);
                               String Trav = FormatDate(Trav_Time);
                               
                              

                               Send_Message2("\u000303"+ Unit.Key+ "\u000304 has to leave at \u000303 "+ Part_Time +". Takes \u000306 "+ Trav +"\u000304 to get there.", Member.Username);
                           }
                       }
                   }

                    
                }
                if (To_Late.Count > 0)
                {
                    String Late = "The following troops will never reach it on time : ";
                    foreach (KeyValuePair<String, int> Val in To_Late)
                    {
                       Late += Val.Key + ", ";
                    }
                    Late = Late.Substring(0, Late.Length - 1);
                    Send_Message2(Late, Member.Username);
                }
            }
            catch
            {
                Send_Message2("Invalid Coords specified ..", Channel);
            }
        }
        
        public void Kick(String Channel, User Member,String Person, String Reason)
        {
            base.Kick(Person, Channel,Reason);
        }

        private String FormatDate(TimeSpan Time)
        {
            String Formatted_Date = String.Empty;
            if (Time.Hours > 0)
                Formatted_Date += Time.Hours.ToString() + ":";
            else
                Formatted_Date += "0" + ":";

            if (Time.Minutes < 10 && Time.Minutes > 0)
                Formatted_Date += "0" + Time.Minutes.ToString();
            else if (Time.Minutes == 0)
                Formatted_Date += "00";
            else
                Formatted_Date += Time.Minutes.ToString();

            Formatted_Date += ":";

            if (Time.Seconds < 10 && Time.Seconds > 0)
                Formatted_Date += "0" + Time.Seconds.ToString();
            else if (Time.Seconds == 0)
                Formatted_Date += "00";
            else
                Formatted_Date+= Time.Seconds.ToString();

            return Formatted_Date;
        }
        public static bool isCommand(String Line)
        {
            //(?:#[A-z]+\s):![A-Za-z^(\s#)]+[^@-](?=[A-z0-9\s])

            Regex Pat = new Regex(@"(?<=[A-z]+\s#[A-z\.]+\s:)\![A-z]");
            return Pat.IsMatch(Line);
            

        }
        protected String[] getParameters(String Line)
        {
            if (getMessage(Line).IndexOf(' ') != -1)
            {
                String Message = getMessage(Line).Trim();
                String[] res = Message.Split(' ');
                String[] results = new String[res.Length - 1];
                for (int i = 1; i < res.Length; i++)
                    results[i-1] = res[i];

                return results;
            }
            return null;
            
        }

        protected object[] CombineParameters(String Insert, User MemberName, String[] To_combine)
        {
            object[] result = new object[3];
            result[0] = Insert;
            result[1] = MemberName;
            String Formatted_Params = String.Join(",", To_combine);
            result[2] = Formatted_Params;

            return result;
        }
        public static String getCommand(String Line,bool IRCCommand = false)
        {
            Regex CommandReg = new Regex(@"(?<!=\!).+(?=\s)");
            Regex IRCCommandReg = new Regex(@"(?<=^:[A-z\!@\.\-0-9]+\s)[A-z0-9]+(?=\s#?[A-z0-9]+)");
            Match Result = IRCCommand ? IRCCommandReg.Match(Line) : CommandReg.Match(getMessage(Line).Trim());
            return Result.Value.Trim();
        }

        private String Replace(String To_Replace,char Sign)
        {
            String Target = String.Empty;
            foreach (String Parameter in To_Replace.Split(','))
            {
                Target = String.Concat(Target, Parameter, Sign);

            }
            Target = Target.Remove(Target.Length - 1);
            return Target;
        }
        private static String Capitalize(String Text)
        {
            try
            {
                if (Text.Length >= 2)
                    return Text = char.ToUpper(Text[0]) + Text.Substring(1).ToLower();
                else
                    return char.ToUpper(Text[0]).ToString();
            }
            catch
            {
                return String.Empty;
            }
        }
        private double getDistance (double x1, double y1, double x2, double y2){

            return Math.Pow((Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2)), .5);

        }
        private TimeSpan getTime(double Val)
        {
            int hour = (int)Val;
            Val -= hour;
            int minutes = (int)(Val * 60);
            Val -= minutes / 60.0;
            int seconds = (int)(Val * 3600);
            TimeSpan Tijd = new TimeSpan(hour, minutes, seconds);

            return Tijd;
        }
      
    }
}
